<!DOCTYPE html>
<html lang="en">
	<head>
	  <meta charset="UTF-8">
	  <meta name="viewport" content="width=device-width, initial-scale=1.0">
	  <meta http-equiv="X-UA-Compatible" content="ie=edge">
	  <title>Document</title>
	</head>
	<body>
		<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
		<script src="https://cdn.bootcss.com/require.js/2.3.6/require.min.js"></script>
		<script>
			// let s = Symbol();
			// console.log(typeof s);//symbol

			// let s1 = Symbol('foo');
			// let s2 = Symbol('bar');
			// console.log(s1);// Symbol(foo)
			// console.log(s2);// Symbol(bar)
			// console.log(s1.toString());// "Symbol(foo)"
			// console.log(s2.toString());// "Symbol(bar)"

			// const obj ={
			// 	toString(){
			// 		return 'abc';
			// 	}
			// };
			// const sym = Symbol(obj);
			// console.log(sym);//Symbol(abc)


			// 相同参数的Symbol函数的返回值是不相等的
			// let s1 = Symbol();
			// let s2 = Symbol();
			// console.log(s1 === s2);//false

			// let s1 = Symbol('foo');
			// let s2 = Symbol('foo');
			// console.log(s1 === s2);//false

			//Symbol 值不能与其他类型的值进行运算，会报错
			// let sym = Symbol('My symbol');
			// 'your symbol is' + sym;//Uncaught TypeError: Cannot convert a Symbol value to a string
			// `your symbol is ${sym}`;//Uncaught TypeError: Cannot convert a Symbol value to a string

			// Symbol 值可以显式转为字符串
			// let sym = Symbol('My symbol');
			// console.log(String(sym));// 'Symbol(My symbol)'
			// console.log(sym.toString());// 'Symbol(My symbol)'

			// let sym = Symbol();
			// console.log(Boolean(sym));// true
			// console.log(!sym);// false
			// if(sym){
			// 	// ...
			// }
			// // console.log(Number(sym));// TypeError
			// console.log(sym + 2);// TypeError


			// let mySymbol = Symbol();
			// 第一种写法
			// let a = {};
			// a[mySymbol] = 'Hello';
			// 第二种写法
			// let a = {
			// 	[mySymbol]:'Hello'
			// }
			// 第三种写法
			// let a = {};
			// Object.defineProperty(a,mySymbol,{value:'Hello'});
			// console.log(a[mySymbol]);// "Hello!"

			// 点运算符后面总是字符串，所以不会读取mySymbol作为标识名所指代的那个值，导致a的属性名实际上是一个字符串，而不是一个 Symbol 值
			// const mySymbol = Symbol();
			// const a = {};
			// a.mySymbol = 'Hello';
			// console.log(a[mySymbol]);// undefined
			// console.log(a['mySymbol']);// "Hello!"



			// let s = Symbol();
			// let obj = {
			//     [s]: function (arg) { 
			//     //... 
			// 	}
			// };
			//或者
			// let obj = {
			//     [s](arg) { 
			//     	// ... 
			//     }
			// };
			// console.log(obj[s](123));//undefined

			// const log = {};
			// log.levels = {
			// 	DEBUG:Symbol('debug'),
			// 	INFO:Symbol('info'),
			// 	WARN:Symbol('warn')
			// };
			// console.log(log.levels.DEBUG,'debug message');//Symbol(debug) "debug message"
			// console.log(log.levels.INFO,'info message');//Symbol(info) "info message"

			// 常量使用 Symbol 值最大的好处，就是其他任何值都不可能有相同的值了，因此可以保证上面的switch语句会按设计的方式工作
			// const COLOR_RED = Symbol();
			// const COLOR_GREEN = Symbol();
			// function getComplement(color){
			// 	switch(color){
			// 		case COLOR_GREEN:
			// 			return COLOR_RED;
			// 		case COLOR_RED:
			// 			return COLOR_GREEN;
			// 		default:
			// 			throw new Error('undefined color');
			// 	}
			// }

			// 魔术字符串指的是，在代码之中多次出现、与代码形成强耦合的某一个具体的字符串或者数值
			// function getArea(shape,options){
			// 	let area = 0;
			// 	switch(shape){
			// 		case 'Triangle':
			// 		area = .5*options.width * options.height;
			// 		break;
			// 	}
			// 	return area;
			// }
			// getArea('Triangle',{width:100,heigth:100});

			// 常用的消除魔术字符串的方法，就是把它写成一个变量
			// const shapeType = {
			// 	triangle:'Triangle'
			// };
			// function getArea(shape,options){
			// 	let area = 0;
			// 	switch(shape){
			// 		case shapeType.triangle:
			// 			area = .5 * options.width * options.height;
			// 		break;
			// 	}
			// 	return area;
			// }
			// getArea(shapeType.triangle,{ width:100,height:100});
			// Triangle写成shapeType对象的triangle属性，消除了强耦合,将shapeType.triangle的值设为一个 Symbol,确保不会跟其他shapeType属性的值冲突即可
			// const shapeType = {
			// 	triangle:Symbol();
			// }

			// const obj = {};
			// let a = Symbol('a');
			// let b = Symbol('b');
			// obj[a] = 'Hello';
			// obj[b] = 'World';
			// const objectSymbol = Object.getOwnPropertySymbols(obj);
			// console.log(objectSymbol);//[Symbol(a), Symbol(b)]

			// const obj = {};
			// let foo = Symbol('foo');
			// Object.defineProperty(obj,foo,{value:'foobar'});
			// for(let i in obj){
			// 	console.log(i);// 无输出
			// }
			// console.log(Object.getOwnPropertyNames(obj));// []
			// console.log(Object.getOwnPropertySymbols(obj));// [Symbol(foo)]

			// let obj = {
			//     [Symbol('my_key')]: 1,
			//     enum: 2,
			//     nonEnum: 3
			// };
			// console.log(Reflect.ownKeys(obj));//["enum", "nonEnum", Symbol(my_key)]

			// let size = Symbol('size');
			// class Collection{
			// 	constructor(){
			// 		this[size] = 0;
			// 	}
			// 	add(item){
			// 		this[this[size]] = item;
			// 		this[size]++;
			// 	}
			// 	static sizeOf(instance){
			// 		return instance[size];
			// 	}
			// }
			// let x  =new Collection();
			// console.log(Collection.sizeOf(x));//0
			// console.log(x.add('foo'));//undefined
			// console.log(Collection.sizeOf(x));//1
			// console.log(Object.keys(x));// ['0']
			// console.log(Object.getOwnPropertyNames(x));// ['0']
			// console.log(Object.getOwnPropertySymbols(x)); // [Symbol(size)]

			// Symbol.for()会被登记在全局环境中供搜索,不会每次调用就返回一个新的 Symbol 类型的值，而是会先检查给定的key是否已经存在，如果不存在才会新建一个值
			// let s1 = Symbol.for('foo');
			// let s2 = Symbol.for('foo');
			// console.log(s1 === s2);//true

			// console.log(Symbol.for('bar') === Symbol.for('bar'));//true
			// console.log(Symbol('bar') === Symbol('bar'));//false

			// let s1 = Symbol.for('foo');
			// console.log(Symbol.keyFor(s1));//foo
			// let s2 = Symbol.for('foo');
			// console.log(Symbol.keyFor(s2));//foo
			// let s3 = Symbol('foo');
			// console.log(Symbol.keyFor(s3));//undefined

			// iframe = document.createElement('iframe');
			// iframe.src = String(window.location);
			// document.body.appendChild(iframe);
			// iframe.contentWindow.Symbol.for('foo') === Symbol.for('foo');

			// const a = require(['./mod.js']);
			// console.log(a.foo);

			// class MyClass {
			//     [Symbol.hasInstance](foo) {
			//     return foo instanceof Array;
			//     }
			// }
			// console.log([1, 2, 3] instanceof new MyClass());//true


			// class Even {
			// 	static [Symbol.hasInstance](obj) {
			// 		return Number(obj) % 2 === 0;
			// 	}
			// }
			//等同于
			// const Even = {
			// 	[Symbol.hasInstance](obj) {
			// 		return Number(obj) % 2 === 0;
			// 	}
			// }
			// console.log(1 instanceof Even);//false
			// console.log(2 instanceof Even);//true
			// console.log(12345 instanceof Even);//false

			// let arr1 = ['c','d'];
			// console.log(['a','b'].concat(arr1,'e'));//["a", "b", "c", "d", "e"]
			// console.log(arr1[Symbol.isConcatSpreadable]);//undefined

			// let arr2 = ['c','d'];
			// arr2[Symbol.isConcatSpreadable] = false;
			// console.log(['a','b'].concat(arr2,'e'));// ['a', 'b', ['c','d'], 'e']

			// let arr2 = ['c','d'];
			// arr2[Symbol.isConcatSpreadable] = true;
			// console.log(['a','b'].concat(arr2,'e'));// ["a", "b", "c", "d", "e"]

			// let obj = {length:2,0:'c',1:'d'};
			// console.log(['a','b'].concat(obj,'e'));// ['a', 'b', obj, 'e']
			// obj[Symbol.isConcatSpreadable] = true;
			// console.log(['a','b'].concat(obj,'e'));//["a", "b", "c", "d", "e"]

			//Symbol.isConcatSpreadable的位置差异，A1是定义在实例上，A2是定义在类本身，效果相同
			// class A1 extends Array {
			// 	constructor(args) {
			// 		super(args);
			// 		this[Symbol.isConcatSpreadable] = true;
			// 	}
			// }
			// class A2 extends Array {
			// 	constructor(args) {
			// 		super(args);
			// 	}
			// 	get [Symbol.isConcatSpreadable] () {
			// 		return false;
			// 	}
			// }
			// let a1 = new A1();
			// a1[0] = 3;
			// a1[1] = 4;
			// let a2 = new A2();
			// a2[0] = 5;
			// a2[1] = 6;
			// console.log([1,2].concat(a1).concat(a2));// [1, 2, 3, 4, [5, 6]]

			// class MyArray extends Array {
			// }
			// const a = new MyArray(1,2,3);
			// const b = a.map(x =>x);
			// const c = a.filter(x => x>1);

			// console.log(b);//MyArray(3) [1, 2, 3]
			// console.log(c);//MyArray(2) [ 2, 3]
			// console.log(b instanceof MyArray);//true
			// console.log(c instanceof MyArray);//true

			// class MyArray extends Array {
			// 	static get [Symbol.species]() {
			// 		return Array;
			// 	}
			// }

			// class MyArray extends Array {
			// 	static get [Symbol.species]() {
			// 		return Array;
			// 	}
			// }
			// const a = new MyArray();
			// const b = a.map(x => x);
			// console.log(b instanceof MyArray);//false
			// console.log(b instanceof Array);//true

			// class T1 extends Promise {
			// }

			// class T2 extends Promise {
			// 	static get [Symbol.species]() {
			// 		return Promise;
			// 	}
			// }
			// console.log(new T1(r => r()).then(v => v) instanceof T1);//true
			// console.log(new T2(r => r()).then(v => v) instanceof T2);//false

			// String.prototype.match(regexp)
			//等同于
			// regexp[Symbol.match](this)
			// class MyMatcher {
			// 	[Symbol.match](string) {
			// 		return 'hello world'.indexOf(string);
			// 	}
			// }
			// console.log('e'.match(new MyMatcher()));//1

			





		</script>
	</body>
</html>